#include "fractioninterpreter.h"

#pragma region Konstruktory i destruktor

FractionInterpreter::FractionInterpreter(QObject *parent)
	: QObject(parent)
{
	_syntax = new SyntaxAnalyser(this);
}

FractionInterpreter::FractionInterpreter(QString &code, QObject *parent)
	: QObject(parent)	
{
	_syntax = new SyntaxAnalyser(code, this);
}

FractionInterpreter::~FractionInterpreter()
{

}

#pragma endregion

SyntaxAnalyser* FractionInterpreter::syntax()
{
	return _syntax;
}

void FractionInterpreter::setSource(QString &source)
{
	//qDebug() << "///TODO: FractionInterpreter::setSource(QString &source)";

	_syntax->setSource(source);
	scopes.clear();
	if( _syntax->Program() )
	{
		qDebug() << "SUKCES!";
		setData();
		if(ExecuteBlock(root))
			qDebug() << "Wykonanie zakonczone sukcesem!";
	}
	else qDebug() << "POPRAW TO!";
}

SyntaxTreeNode* FractionInterpreter::tree()
{
	return _syntax->tree();
}

void FractionInterpreter::setData()
{
	root = _syntax->executionPartNode();
	global = _syntax->loadGlobalScope();
	scopes.append(_syntax->loadMainScope());

	qDebug() << "Globals:";
	for(int i = 0; i < global.vars().size(); ++i)
	{
		qDebug() << "Zmienna " << i << " ID: " << (global.vars())[(i)].id() << " Value: " << (global.vars())[i].value().toString();
	}
	qDebug() << "Lokals:";
	for(int i = 0; i < scopes[0].vars().size(); ++i)
	{
		qDebug() << "Zmienna " << i << " ID: " << (scopes[0].vars())[(i)].id() << " Value: " << (scopes[0].vars())[i].value().toString();
	}
}

qint32 FractionInterpreter::startProcedure(Procedure p)
{
	scopes.append(_syntax->loadScope(p)); // dodanie widoku do listy widokow

	for(int i = 0; i < p.args().size(); ++i)
	{
		scopes.last().assign(i + 1, p.arg(i).id());		
	}

	qDebug() << "*****************************";
	qDebug() << "Starting procedure " << p.id();		
	for(int i = 0; i < scopes.last().vars().size(); ++i)
	{
		qDebug() << "arg " << i << ": " << (scopes.last().vars())[i].id() << " " <<  (scopes.last().vars())[i].value().toString();
	}

	return scopes.size();
}

Value FractionInterpreter::endProcedure(qint32 id)
{
	if(scopes.size() != id)
		qDebug() << "ERR: Cos nie tak z wywolaniami funkcji!";

	Value v = scopes.last().retValue();

	scopes.removeAt(scopes.size() - 1);

	return v;
}

#pragma region ExecuteBlock()

bool FractionInterpreter::ExecuteBlock(SyntaxTreeNode *node)
{
	Value v1;

	for(int i = 0; i < node->childrenCount(); ++i)
	{
		QString s = node->childAt(i)->symbol().value();

		if(s == tr("Statments") )
		{
			if( !ExecuteStatments(node->childAt(i)) ) return false;
			
		}
		else if(s == tr("return"))
		{
			v1 =  ExecuteExpression(node->childAt(i + 1));
			if(v1.type() == HaveNoIdeaType) return false;
			qDebug() << "Returned Value: " << v1.toString();
			scopes.last().setRetValue(v1);
			return true;
		}
	}
	return true;	
}

#pragma endregion

#pragma region ExecuteStatments()

bool FractionInterpreter::ExecuteStatments(SyntaxTreeNode *node)
{
	if(node->childrenCount() == 0)
	{
		qDebug() << "No statments!";
		return true;
	}

	Value v1;

	for(int i = 0; i < node->childrenCount(); i = i + 2)
	{
		QString s = node->childAt(i)->symbol().value();
		if(s == tr("Assigment"))
		{
			if( !ExecuteAssigment(node->childAt(i)) ) return false;
		}
		else if(s == tr("ProcedureCall"))
		{
			v1 = ExecuteProcedureCall(node->childAt(i));
			if(v1.type() == HaveNoIdeaType) return false;
		}
		else if(s == tr("GetVarCall"))
		{
			v1 = ExecuteGetVarCall(node->childAt(i));
			if(v1.type() == HaveNoIdeaType) return false;
		}
		else if(s == tr("PrintVarCall"))
		{
			if( !ExecutePrintVarCall(node->childAt(i))) return false;			
		}
		else if(s == tr("IfStatment"))
		{
			if( !ExecuteIfStatment(node->childAt(i))) return false;
		}
		else if(s == tr("WhileStatment"))
		{
			if( !ExecuteWhileStatment(node->childAt(i))) return false;
		}
		else
		{
			qDebug() << "ERR: Nieznany symbol!";
			return false;
		}
	}
	return true;
}

#pragma endregion

#pragma region ExecuteExpression()

Value FractionInterpreter::ExecuteExpression(SyntaxTreeNode *node)
{
	Value v1, v2;

	if(node->childrenCount() == 1)
	{
		v1 = ExecuteSimpleExpression(node->childAt(0));
		return v1;
	}
	else if(node->childrenCount() == 3)
	{		
		SymbolType rel;
		v1 = ExecuteSimpleExpression(node->childAt(0));
		rel = ExecuteRelOp(node->childAt(1));
		v2  = ExecuteSimpleExpression(node->childAt(2));

		if(rel == GreaterEqualSym)
		{
			Fraction f1(v1.toString());
			Fraction f2(v2.toString());

			if(f1 >= f2)
				return Value(BooleanType, tr("true"));
			else
				return Value(BooleanType, tr("false"));
		}
		else if(rel == GreaterSym)
		{
			Fraction f1(v1.toString());
			Fraction f2(v2.toString());

			if(f1 > f2)
				return Value(BooleanType, tr("true"));
			else
				return Value(BooleanType, tr("false"));
		}
		else if(rel == LessEqualSym)
		{
			Fraction f1(v1.toString());
			Fraction f2(v2.toString());

			if(f1 <= f2)
				return Value(BooleanType, tr("true"));
			else
				return Value(BooleanType, tr("false"));
		}
		else if(rel == LessSym)
		{
			Fraction f1(v1.toString());
			Fraction f2(v2.toString());

			if(f1 < f2)
				return Value(BooleanType, tr("true"));
			else
				return Value(BooleanType, tr("false"));
		}
		else if(rel == EqualSym)
		{
			if(v1.toString() == v2.toString())
				return Value(BooleanType, tr("true"));
			else
				return Value(BooleanType, tr("false"));
		}
		else if(rel == NotEqualSym)
		{
			if(v1.toString() != v2.toString())
				return Value(BooleanType, tr("true"));
			else
				return Value(BooleanType, tr("false"));
		}
		else 
		{
			qDebug() << "ERR: Unknown RelOp!";
			return Value(HaveNoIdeaType, tr("err: execute expression!"));
		}
	}
	else 
	{
		qDebug() << "ERR: too many children (Expression)";
		return Value(HaveNoIdeaType, tr("err: execute expression!"));
	}	
}

#pragma endregion

#pragma region ExecuteSimpleExpression()

//SimpleExp		= MinorFactor { MinorOp MinorFactor }

Value FractionInterpreter::ExecuteSimpleExpression(SyntaxTreeNode *node)
{
	Value v1, v2;
	SymbolType rel;
	Fraction wynik_f;
	bool wynik_b;

	v1 = ExecuteMinorFactor(node->childAt(0));

	if(v1.type() == HaveNoIdeaType) return v1;

	if(v1.type() == BooleanType)
	{
		wynik_b = v1.toBoolean();
	}
	else
		wynik_f = v1.toFraction();

	for(int i = 1; i < node->childrenCount(); i = i + 2)
	{
		rel = ExecuteMinorOp(node->childAt(i));
		v2  = ExecuteMinorFactor(node->childAt(i + 1));

		if(v2.type() == HaveNoIdeaType) return v2;

		if(rel == PlusSym)
		{			
			Fraction f2(v2.toString());
			wynik_f += f2;			
		}
		else if(rel == MinusSym)
		{
			Fraction f2(v2.toString());
			wynik_f -= f2;			
		}
		else if(rel == OrSym)
		{
			if(wynik_b == true || v2.toBoolean() == true)
				wynik_b = true;
			else
				wynik_b = false;
		}
		else
		{			
			qDebug() << "ERR: Unknown MinorOp!";		
		}
	}

	if(v1.type() == BooleanType)	
	{
		if(wynik_b == true)
			return Value(BooleanType, tr("true"));	
		else
			return Value(BooleanType, tr("false"));			
	}
	else
		return Value(FractionType, wynik_f.toString());
}

#pragma endregion

#pragma region ExecuteMinorFactor()

Value FractionInterpreter::ExecuteMinorFactor(SyntaxTreeNode *node)
{
	Value v1, v2;
	SymbolType rel;
	Fraction wynik_f;
	bool wynik_b;

	v1 = ExecutePriorFactor(node->childAt(0));

	if(v1.type() == BooleanType)
	{
		wynik_b = v1.toBoolean();
	}
	else
		wynik_f = v1.toFraction();

	for(int i = 1; i < node->childrenCount(); i = i + 2)
	{
		rel = ExecutePriorOp(node->childAt(i));
		v2  = ExecutePriorFactor(node->childAt(i + 1));

		if(rel == MultilpySym)
		{			
			Fraction f2(v2.toString());
			wynik_f *= f2;			
		}
		else if(rel == DivideSym)
		{
			Fraction f2(v2.toString());
			if(f2.isZero() )
			{
				qDebug() << "ERR: Divide by zero err!";
				return Value(HaveNoIdeaType, tr("0"));
			}
			wynik_f /= f2;			
		}
		else if(rel == AndSym)
		{
			if(wynik_b == true && v2.toBoolean() == true)
				wynik_b = true;
			else
				wynik_b = false;
		}
		else
		{			
			qDebug() << "ERR: Unknown MinorOp!";		
		}

	}

	if(v1.type() == BooleanType)	
	{
		if(wynik_b == true)
			return Value(BooleanType, tr("true"));	
		else
			return Value(BooleanType, tr("false"));			
	}
	else
		return Value(FractionType, wynik_f.toString());
}

#pragma endregion

#pragma region ExecutePriorFactor()

// PriorFactor	= [Sign] ProcedureCall | [Sign] id | [Sign] fraction_const | bool_const | [Sign] '(' Expression ')' | 'not' PriorFactor

Value FractionInterpreter::ExecutePriorFactor(SyntaxTreeNode *node)
{
	Symbol t = node->childAt(0)->symbol();
	Value v;
	Fraction wyn_f;
	bool wyn_b;
	// Sign
	if( t.type() == PlusSym || t.type() == MinusSym)
	{
		Symbol s = node->childAt(1)->symbol();		
		
		if(s.value() == tr("ProcedureCall"))
		{
			v = ExecuteProcedureCall(node->childAt(1));			
			wyn_f = v.toFraction();
		}
		else if(s.type() == IdSym)
		{
			v = ExecuteID(node->childAt(1));			
			wyn_f = v.toFraction();
		}
		else if(s.type() == LBracketSym)
		{
			v = ExecuteExpression(node->childAt(2));
			wyn_f = v.toFraction();
		}
		else if(s.type() == FractionConstSym)
		{
			wyn_f = Fraction(s.value());
		}
		
		if(t.type() == MinusSym) wyn_f.changeSign();

		return Value(FractionType, wyn_f.toString());
	}
	else if(t.value() == tr("ProcedureCall"))
	{
		return ExecuteProcedureCall(node->childAt(0));
	}
	else if(t.type() == IdSym)
	{
		return ExecuteID(node->childAt(0));
	}
	else if(t.type() == FractionConstSym)
	{
		return Value(FractionType, t.value());
	}
	else if(t.type() == BoolConstSym)
	{
		return Value(BooleanType, t.value());
	}
	else if(t.type() == LBracketSym)
	{
		return ExecuteExpression(node->childAt(1));		
	}
	else if(t.type() == NotSym)
	{
		v = ExecutePriorFactor(node->childAt(1));

		if( !v.isBoolean() ) 
		{
			qDebug() << "ERR: ExecutePriorFactor";
			return Value(HaveNoIdeaType, tr("ExecutePriorFactor"));
		}
		if( v.toBoolean() == true)
		{
			return Value(BooleanType, tr("false"));
		}
		else
			return Value(BooleanType, tr("true"));
	}
	else
	{
		qDebug() << "ERR: ExecutePriorFactor";
		return Value(HaveNoIdeaType, tr("ExecutePriorFactor"));
	}
	return Value();
}

#pragma endregion

#pragma region ExecuteAssigment()

bool FractionInterpreter::ExecuteAssigment(SyntaxTreeNode *node)
{
	QString s = node->childAt(0)->symbol().value();
	Value v = ExecuteExpression(node->childAt(2));

	if(v.type() == HaveNoIdeaType)
		return false;

	if(scopes.last().exists(s))
	{
		scopes.last().assign(s, v.toString());
		return true;
	}
	else if(global.exists(s))
	{
		global.assign(s, v.toString());
		return true;
	}
	else
	{
		qDebug() << "ERR: Assign. Unknown ID: " << s;
		return false;
	}	
}

#pragma endregion

#pragma region ExecuteIfStatment()

//IfStatment = 'if' '(' Expression ')' 'then' Statments [ 'else' Statments ] 'endif'

bool FractionInterpreter::ExecuteIfStatment(SyntaxTreeNode *node)
{
	Value v1 = ExecuteExpression(node->childAt(2));
	if(v1.type() == HaveNoIdeaType)
		return false;

	bool ok;

	if(v1.isBoolean())
		ok = v1.toBoolean();
	else
		ok = !v1.toFraction().isZero();

	if(ok)
	{
		if(ExecuteStatments(node->childAt(5)))
			return true;
		else
			return false;
	}
	else
	{
		if(node->childrenCount() == 9)
		{
			if(ExecuteStatments(node->childAt(7)))
				return true;
			else
				return false;
		}
	}
	return true;
}

#pragma endregion

#pragma region ExecuteWhileStatment()
	
// WhileStatment = 'while' '(' Expression ')' 'do' Statments 'endwhile'

bool FractionInterpreter::ExecuteWhileStatment(SyntaxTreeNode *node)
{
	Value v1 = ExecuteExpression(node->childAt(2));
	if(v1.type() == HaveNoIdeaType)
		return false;

	bool ok;

	if(v1.isBoolean())
		ok = v1.toBoolean();
	else
		ok = !v1.toFraction().isZero();

	while(ok)
	{
		if( !ExecuteStatments(node->childAt(5)))
			return false;

		v1 = ExecuteExpression(node->childAt(2));
		if(v1.type() == HaveNoIdeaType)
			return false;

		if(v1.isBoolean())
			ok = v1.toBoolean();
		else
			ok = !v1.toFraction().isZero();
	}
	return true;
}

#pragma endregion

#pragma region ExecuteGetVarCall()

// GetVarCall = 'getFraction '(' id ')'

Value FractionInterpreter::ExecuteGetVarCall(SyntaxTreeNode *node)
{
	QString s = node->childAt(2)->symbol().value();
	
	QString s1;
	
	GetFractionDialog dialog;
	while(dialog.exec() != QDialog::Accepted) {};

	s1 = dialog.getFraction().toString();	

	if(scopes.last().exists(s))
	{
		scopes.last().assign(s, s1);		
	}
	else if(global.exists(s))
	{
		global.assign(s, s1);		
	}
	else
	{
		qDebug() << "ERR: Assign. Unknown ID: " << s;
		return Value(HaveNoIdeaType, tr("ERR: ExecuteGetVarCall"));
	}	

	return Value(FractionType, s1);
}

#pragma endregion

#pragma region ExecutePrintVarCall()

// PrintVarCall = 'print' '('  Expression | Text ')'

bool FractionInterpreter::ExecutePrintVarCall(SyntaxTreeNode *node)
{
	if(node->childAt(2)->symbol().type() == TextSym)
	{
		qDebug() << "Print: " << node->childAt(2)->symbol().value();		
	}
	else
	{
		Value v1 = ExecuteExpression(node->childAt(2));
		if(v1.type() == HaveNoIdeaType)
			return false;

		qDebug() << "Print: " << v1.toString();
	}

	return true;
}

#pragma endregion

#pragma region ExecuteMinorOp()

SymbolType FractionInterpreter::ExecuteMinorOp(SyntaxTreeNode *node)
{
	return node->childAt(0)->symbol().type();
}

#pragma endregion

#pragma region ExecutePriorOp()

SymbolType FractionInterpreter::ExecutePriorOp(SyntaxTreeNode *node)
{
	return node->childAt(0)->symbol().type();
}

#pragma endregion

#pragma region ExecuteRelOp()

SymbolType FractionInterpreter::ExecuteRelOp(SyntaxTreeNode *node)
{
	return node->childAt(0)->symbol().type();
}

#pragma endregion

#pragma region ExecuteProcedureCall()

// ProcedureCall = id '(' [ Expression { ',' Expression } ] ')'

Value FractionInterpreter::ExecuteProcedureCall(SyntaxTreeNode* node)
{
	Procedure p;				// procedura za pomoca ktorej zidentyfikuje o jaka procedure chodzi 

	p.setID(node->childAt(0)->symbol().value());	// ustawiam id

	for(int i = 2; i < node->childrenCount(); i = i + 2)  // ustawianie typow
	{
		Value v1 = ExecuteExpression(node->childAt(i));
		if(v1.type() == HaveNoIdeaType)
			return v1;

		p.addArg(Arg(v1.type(), v1.toString()), true);
	}	

	qint32 id = startProcedure(p);		

		if(!ExecuteBlock(_syntax->procedure(p)))
			return Value(HaveNoIdeaType, tr("ERR: ExecuteProcedureCall: ") + p.id() );

	return endProcedure(id);
}

#pragma endregion

#pragma region ExecuteID()

Value FractionInterpreter::ExecuteID(SyntaxTreeNode *node)
{
	Value v;

	if(scopes.last().exists(node->symbol().value()) )
	{
		v = scopes.last().value(node->symbol().value());
	}
	else if(global.exists(node->symbol().value()) )
	{
		v = global.value(node->symbol().value());
	}
	else
	{
		qDebug() << "ERR: ExecuteID - no matching id in scope";
	}

	if(v.toString().isEmpty())
	{
		if(v.isFraction())
			v.setValue(tr("0"));
		else
			v.setValue(tr("true"));
	}
	
	return v;
}

#pragma endregion